package engineer.view.entity_model;

import engineer.Engineer;
import entity_model.Attribute;
import entity_model.Entity;
import entity_model.EntityModel;
import entity_model.Relationship;
import fx.FX;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;
import laravel.Laravel;
import utils.Utils;
import utils.titles.Title;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import static engineer.reverse.schema.RevertSchema.ExtensionFilters.EEM;
import static entity_model.Relationship.Direction;
import static entity_model.Relationship.Role.Cardinality.ONE;


/**
 * Created by nuno-rafael-figueiredo on 23-03-2014.
 */
public class EntityModelController extends BorderPane {
    @FXML
    private Button saveButton;

    @FXML
    private Button saveAsButton;

    @FXML
    private Button closeButton;

    @FXML
    private Button generateLaravelButton;

    @FXML
    private TreeView treeView;

    @FXML
    private TreeItem<Entity> entitiesTreeItem;

    @FXML
    private TreeItem<Relationship> oneToOneTreeItem;

    @FXML
    private TreeItem<Relationship> oneToManyTreeItem;

    @FXML
    private TreeItem<Relationship> manyToOneTreeItem;

    @FXML
    private TreeItem<Relationship> manyToManyTreeItem;

    @FXML
    private TableView<Attribute> attributesView;

    @FXML
    private TableColumn<Attribute, String> attributeNameCol;

    @FXML
    private TableView<Relationship.Related> relationshipsView;

    @FXML
    private TableColumn<Relationship.Related, Relationship.Role.Cardinality> relationshipMyRoleCardinalityCol;

    @FXML
    private TableColumn<Relationship.Related, Title> relationshipMyRoleTitleCol;

    @FXML
    private TableColumn<Relationship.Related, Relationship.Direction> relationshipDirectionCol;

    @FXML
    private TableColumn<Relationship.Related, Relationship.Role.Cardinality> relationshipTheOthersRoleCardinalityCol;

    @FXML
    private TableColumn<Relationship.Related, Title> relationshipTheOthersRoleTitleCol;

    @FXML
    private Button loadEntityModelButton;

    @FXML
    private Button cancelButton;


    private File file;

    public EntityModelController() {
        FX.load(this);
    }

    private EntityModel entityModel;

    public final Property<Laravel> laravelProperty = new SimpleObjectProperty<>();

    @FXML
    private void initialize(){
        saveButton.setOnAction(e -> save());
        saveAsButton.setOnAction(e -> saveAs());
        closeButton.setOnAction(e -> close());
        generateLaravelButton.setOnAction(e -> generateLaravel());

        treeView.getSelectionModel().selectedItemProperty().addListener((value, before, after) -> onSelect(after));

        attributeNameCol.setCellValueFactory(new PropertyValueFactory<>("name"));

        relationshipMyRoleCardinalityCol.setCellValueFactory(new PropertyValueFactory<>("myRoleCardinality"));
        relationshipMyRoleCardinalityCol.setCellFactory(column -> new RoleCardinalityTableCell(Relationship.Related::getMyRole));

        relationshipMyRoleTitleCol.setCellValueFactory(new PropertyValueFactory<>("myRoleTitle"));
        relationshipMyRoleTitleCol.setCellFactory(column -> new RoleTitleTableCell(Relationship.Related::getMyRole));

        relationshipDirectionCol.setCellValueFactory(new PropertyValueFactory<>("direction"));
        relationshipDirectionCol.setCellFactory(column -> new ComboBoxTableCell<Relationship.Related, Direction>(Direction.values()){
            @Override
            public void commitEdit(Direction direction) {
                super.commitEdit(direction);

                Relationship.Related related = (Relationship.Related) getTableRow().getItem();
                related.setDirection(direction);
            }
        });

        relationshipTheOthersRoleCardinalityCol.setCellValueFactory(new PropertyValueFactory<>("theOthersRoleCardinality"));
        relationshipTheOthersRoleCardinalityCol.setCellFactory(column -> new RoleCardinalityTableCell(Relationship.Related::getTheOthersRole));

        relationshipTheOthersRoleTitleCol.setCellValueFactory(new PropertyValueFactory<>("theOthersRoleTitle"));
        relationshipTheOthersRoleTitleCol.setCellFactory(column -> new RoleTitleTableCell(Relationship.Related::getTheOthersRole));
    }


    public void generateLaravel() {
        Laravel laravel = new Laravel(entityModel);
        laravelProperty.setValue(laravel);
        laravel.execute();
    }

    private void configureFileChooser(FileChooser fileChooser) {
        // TODO
    }

    public void save() {

        if (file == null){
            saveAs();
            return;
        }

        try(PrintWriter writer = new PrintWriter(new FileWriter(file))){
            writer.print(Utils.getGson().toJson(entityModel));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void saveAs() {

        final FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Save entity model");

        fileChooser.setInitialDirectory(Engineer.FOLDER.getFile());
        fileChooser.getExtensionFilters().add(EEM);
        file = fileChooser.showSaveDialog(getScene().getWindow());
        if (file != null)
            save();
    }

    public void close() {

    }

    private void onSelect(Object o) {
        if (o != null && ((TreeItem) o).getValue() instanceof Entity) {
            Entity entity = (Entity) ((TreeItem) o).getValue();

            attributesView.setItems(entity.getAttributes());
            relationshipsView.setItems(entity.getRelateds());

            return;
        }
        attributesView.getItems().clear();
        relationshipsView.getItems().clear();
    }

    public void setEntityModel(EntityModel entityModel) {
        this.entityModel = entityModel;
        for(Entity entity : entityModel.entities)
            entitiesTreeItem.getChildren().add(new TreeItem<>(entity));

        for(Relationship relationship : entityModel.relationships) {
            TreeItem<Relationship> groupTreeItem;
            groupTreeItem = relationship.getLeftRole().getCardinality() == ONE ?
                    relationship.getRightRole().getCardinality() == ONE ?
                            oneToOneTreeItem :
                            oneToManyTreeItem :
                    relationship.getRightRole().getCardinality() == ONE ?
                            manyToOneTreeItem :
                            manyToManyTreeItem;

            final TreeItem<Relationship> relationshipTreeItem = new TreeItem<>(relationship);
            groupTreeItem.getChildren().add(relationshipTreeItem);
        }

    }

    @Override
    public String toString() {
        return entityModel.toString();
    }

    public EntityModel getEntityModel() {
        return entityModel;
    }

    public void setFile(File file) {
        this.file = file;
    }
}
